package com.boot.persistence.controller;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.boot.persistence.condition.QueryCondition;
import com.boot.persistence.model.BaseModel;
import com.boot.persistence.service.IService;
import com.boot.persistence.validate.group.InsertGroup;
import com.boot.persistence.validate.group.UpdateGroup;
import com.boot.web.exception.GlobalException;
import com.boot.web.http.HttpModel;
import com.boot.web.http.Status;
import com.boot.web.util.HttpResponseUtil;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class ControllerImpl<Id extends Serializable, T extends BaseModel<Id>, Service extends IService<Id, T, ? extends BaseMapper<T>>> implements IControllerMapping<Id, T, Service> {

    protected Service service;
    protected Validator validator;

    public ControllerImpl(Service service, Validator validator) {
        this.service = service;
        this.validator = validator;
    }

    @Override
    public ResponseEntity<HttpModel<T>> selectById(Id id, String useStrict) {
        T ans = service.getById(id);
        if (ans != null) {
            return HttpResponseUtil.OK("查询成功", ans);
        }
        if (useStrict(useStrict)) {
            return HttpResponseUtil.notFound("记录不存在");
        }
        return HttpResponseUtil.noContent("记录不存在");
    }

    @Override
    public ResponseEntity<HttpModel<T>> selectOne(T model, String useStrict) {
        T ans = service.getOne(Wrappers.query(model));
        if (ans != null) {
            return HttpResponseUtil.OK("查询成功", ans);
        }
        if (useStrict(useStrict)) {
            return HttpResponseUtil.notFound("记录不存在");
        }
        return HttpResponseUtil.noContent("记录不存在");
    }

    @Override
    public ResponseEntity<HttpModel<Collection<T>>> selectByIds(List<Id> list, String useStrict) {
        if (useStrict(useStrict)) {
            List<T> result = service.listByIds(list);
            if (result.size() == list.size()) {
                return HttpResponseUtil.OK("查询成功", result);
            } else {
                throw new GlobalException(Status.NO_RECORD, HttpStatus.NOT_FOUND);
            }
        } else {
            return HttpResponseUtil.OK("查询成功", service.listByIds(list));
        }
    }

    @Override
    public ResponseEntity<HttpModel<IPage<T>>> select(QueryCondition<Id, T> condition) {
        return HttpResponseUtil.OK("查询成功", service.select(condition));
    }

    @Override
    public ResponseEntity<HttpModel<IPage<T>>> selectLike(QueryCondition<Id, T> condition) {
        return HttpResponseUtil.OK("查询成功", service.selectLike(condition));
    }

    @Override
    public ResponseEntity<HttpModel<IPage<T>>> selectAll() {
        return HttpResponseUtil.OK("查询成功", service.page(new Page<>(1, service.count())));
    }

    @Override
    public ResponseEntity<HttpModel<Long>> selectCount(T model) {
        return HttpResponseUtil.OK("查询成功", service.count(Wrappers.query(model)));
    }

    @Override
    public ResponseEntity<HttpModel<Id>> insertRecord(T model) {
        if (service.save(model)) {
            return HttpResponseUtil.CREATED("插入成功", model.pk());
        }
        return HttpResponseUtil.badRequest("插入失败");
    }

    @Override
    public ResponseEntity<HttpModel<List<Id>>> insertRecords(List<T> list) {
        validate(list, InsertGroup.class);
        if (service.saveBatch(list)) {
            return HttpResponseUtil.CREATED("插入成功", list.stream().map(T::pk).collect(Collectors.toList()));
        }
        return HttpResponseUtil.badRequest("插入失败");
    }

    @Override
    public ResponseEntity<HttpModel<Id>> saveRecord(T model) {
        if (service.saveOrUpdate(model)) {
            return HttpResponseUtil.CREATED("保存成功", model.pk());
        }
        return HttpResponseUtil.badRequest("保存失败");
    }

    @Override
    public ResponseEntity<HttpModel<List<Id>>> saveRecords(List<T> list) {
        validate(list, InsertGroup.class);
        if (service.saveOrUpdateBatch(list)) {
            return HttpResponseUtil.CREATED("保存成功", list.stream().map(T::pk).collect(Collectors.toList()));
        }
        return HttpResponseUtil.badRequest("保存失败");
    }

    @Override
    public ResponseEntity<HttpModel<Void>> updateRecord(T model) {
        if (service.updateById(model)) {
            return HttpResponseUtil.OK("更新成功");
        }
        return HttpResponseUtil.badRequest("更新失败");
    }

    @Override
    public ResponseEntity<HttpModel<Void>> updateRecords(List<T> list) {
        validate(list, UpdateGroup.class);
        if (service.updateBatchById(list)) {
            return HttpResponseUtil.OK("更新成功");
        }
        return HttpResponseUtil.badRequest("更新失败");
    }

    @Override
    public ResponseEntity<HttpModel<Void>> deleteById(Id id, String useStrict) {
        if ((!service.removeById(id)) && useStrict(useStrict)) {
            return HttpResponseUtil.notFound("资源不存在");
        }
        return HttpResponseUtil.noContent("资源不存在");
    }

    @Override
    public ResponseEntity<HttpModel<Void>> deleteByIds(List<Id> list, String useStrict) {
        if ((!service.removeByIds(list)) && useStrict(useStrict)) {
            return HttpResponseUtil.notFound("资源不存在");
        }
        return HttpResponseUtil.noContent("资源不存在");
    }

    @Override
    public Service getService() {
        return service;
    }

    protected boolean useStrict(String useStrict) {
        if (useStrict == null) {
            return true;
        } else {
            return "true".equalsIgnoreCase(useStrict);
        }
    }

    protected <S> void validate(List<S> list, Class<?> group) {
        list.forEach(model -> {
            Set<ConstraintViolation<S>> validate = validator.validate(model, group);
            if (validate.size() > 0) {
                throw new ConstraintViolationException(null, validate);
            }
        });
    }
}
